﻿using System.Collections.Specialized;
using System.Text;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using WebAppIdentity.DbContext;

namespace WebAppIdentity.Middlewares
{
	public class SignValidateMiddleware
	{
		private readonly RequestDelegate _next;

		private readonly IServiceScopeFactory _scopeFactory;

		private readonly IMemoryCache _memoryCache;

		public SignValidateMiddleware(RequestDelegate next, IServiceScopeFactory scopeFactory, IMemoryCache memoryCache1)
		{
			_next = next;
			_scopeFactory = scopeFactory;
			_memoryCache = memoryCache1;
		}

		public async Task InvokeAsync(HttpContext context,IServiceProvider serviceProvider)
		{
			var requestPath = context.Request.Path;
			var exceptUrls = new string[] { "/User/", "/WeatherForecast/", "/swagger/", "/ThirdClient/" };
			if (requestPath.HasValue && exceptUrls.Any(p => requestPath.Value.IndexOf(p) > -1))
			{
				await _next(context);
				return;
			}
			
			if (!context.Request.Headers.TryGetValue("AppKey", out var appKey))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-appKey");
				return;
			}
			if (!context.Request.Headers.TryGetValue("TimeStamp", out var timeStamp) || string.IsNullOrEmpty(timeStamp))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-TimeStamp");
				return;
			}
			if (!context.Request.Headers.TryGetValue("Nonce", out var nonce))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-Nonce");
				return;
			}
			if (!context.Request.Headers.TryGetValue("Sign", out var sign))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-Sign");
				return;
			}

			bool timespanvalidate = double.TryParse(timeStamp.ToString(), out double ts1);
			double ts2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
			double ts = ts2 - ts1;
			bool flag = ts > 1000 * 30;//30秒内有效
			if (flag || !timespanvalidate)
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-timespanvalidate");
				return;
			}

			var cacheKey = "thirdClient_" + appKey;
			if (!_memoryCache.TryGetValue(cacheKey, out var token))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("token验证不通过，可能已经过期，请重新获取token再试-token");
				return;
			}
			var _userContext = serviceProvider.GetRequiredService<UserDbContext>()
					  ?? throw new ArgumentNullException("未获取到_userContext");
			var thirdClient = _userContext.ThirdClients.FirstOrDefault(p => p.AppKey == appKey.ToString());
			if (thirdClient == null)
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签不通过-AppKey");
				return;
			}

			IDictionary<string, string> parameters;
			if (context.Request.Method.Equals(HttpMethod.Get.ToString(), StringComparison.CurrentCultureIgnoreCase))
			{
				parameters = new Dictionary<string, string>();
				foreach (var kvp in context.Request.Query)
				{
					parameters.Add(kvp.Key, kvp.Value.ToString());
				}
			}
			else if (context.Request.Method.Equals(HttpMethod.Post.ToString(), StringComparison.CurrentCultureIgnoreCase))
			{
				var requestData = await new StreamReader(context.Request.Body).ReadToEndAsync();
				parameters = JsonConvert.DeserializeObject<Dictionary<string, string>>(requestData ?? "{}") ?? [];

				// 将修改后的请求体重新写入HttpContext，防止action拿不到数据报错
				if (!string.IsNullOrEmpty(requestData))
				{
					byte[] requestBodyBytes = Encoding.UTF8.GetBytes(requestData);
					context.Request.Body = new MemoryStream(requestBodyBytes);
				}				
			}
			else
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("不能识别的请求方法");
				return;
			}

			string requestDataStr = GetSortedRequestDataStr(parameters); //请求参数字符串拼接
			string computedSign = Md5Helper.ComputeMd5Hash(timeStamp.ToString() + nonce.ToString() + appKey.ToString() + token.ToString() + requestDataStr);
			if (!sign.ToString().Equals(computedSign, StringComparison.CurrentCultureIgnoreCase))
			{
				context.Response.StatusCode = StatusCodes.Status406NotAcceptable;
				await context.Response.WriteAsync("验签验证失败");
				return;
			}
			await _next(context);

		}
		public string GetSortedRequestDataStr(IDictionary<string, string> parameters)
		{
			IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
			IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();

			StringBuilder query = new StringBuilder();
			while (dem.MoveNext())
			{
				string key = dem.Current.Key;
				string value = dem.Current.Value;
				if (!string.IsNullOrEmpty(key))
				{
					query.Append(key).Append(value);
				}
			}
			return query.ToString();
		}
	}
}
